package com.jingdianjichi.subject.application.interceptor;

import com.jingdianjichi.subject.infra.rpc.context.LoginContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登录信息拦截器
 * 
 * 该拦截器用于从HTTP请求头中提取用户信息并存储到ThreadLocal中，
 * 使得业务代码可以方便地获取当前登录用户的信息。
 * 
 * 工作流程：
 * 1. preHandle: 请求处理前，从请求头提取loginId并存储到ThreadLocal
 * 2. 业务处理: Controller和Service可以通过LoginUtil获取用户信息
 * 3. afterCompletion: 请求处理完成后，清理ThreadLocal防止内存泄漏
 * 
 * 支持的请求头：
 * - loginId: 网关传递的用户ID
 * 
 * 异常处理：
 * - 如果请求头中没有loginId，不会阻止请求继续处理
 * - 业务代码可以通过LoginUtil.isLogin()检查用户是否登录
 * 
 * 性能考虑：
 * - 拦截器逻辑简洁，对性能影响极小
 * - ThreadLocal操作开销很低
 * - 只在需要时记录debug日志
 *
 * @author: CodeBuddy
 * @date: 2025/9/19
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    /**
     * 用户ID请求头名称
     * 与网关中LoginIdTransferFilter设置的请求头名称保持一致
     */
    private static final String LOGIN_ID_HEADER = "loginId";
    
    /**
     * 请求处理前的拦截方法
     * 
     * 从请求头中提取loginId并存储到ThreadLocal中。
     * 这个方法在Controller方法执行前调用。
     * 
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     * @param handler 处理器对象
     * @return true-继续处理请求，false-中断请求处理
     * @throws Exception 处理异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("准备拦截请求，URI: {}", request.getRequestURI());

        // 从请求头中获取loginId
        String loginId = request.getHeader(LOGIN_ID_HEADER);

        log.info("请求头中获取到的loginId: {}, URI: {}", loginId, request.getRequestURI());
        
        if (StringUtils.hasText(loginId)) {
            // 将用户信息存储到ThreadLocal中
            LoginContextHolder.set(loginId);
            log.debug("设置当前线程用户信息，loginId: {}, URI: {}", loginId, request.getRequestURI());
        } else {
            log.debug("请求头中未找到loginId信息，URI: {}", request.getRequestURI());
        }
        
        // 继续处理请求
        return true;
    }
    
    /**
     * 请求处理完成后的清理方法
     * 
     * 清除ThreadLocal中的用户信息，防止内存泄漏。
     * 这个方法在整个请求处理完成后调用，包括视图渲染完成后。
     * 
     * 重要：无论请求处理成功还是失败，都会调用这个方法，
     * 确保ThreadLocal一定会被清理。
     * 
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     * @param handler 处理器对象
     * @param ex 如果处理过程中发生异常，这里会传入异常对象
     * @throws Exception 处理异常
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        try {
            // 获取当前用户信息用于日志记录
            String loginId = LoginContextHolder.getLoginId();
            if (loginId != null) {
                log.debug("清除当前线程用户信息，loginId: {}, URI: {}", loginId, request.getRequestURI());
            }
        } finally {
            // 无论如何都要清除ThreadLocal，防止内存泄漏
            LoginContextHolder.clear();
        }
    }
}